#include "Application.h"
#include <string>
#include "../SDLtoolkit/widgets/PlayScenarioPanel.h"
#include "../gui/WindowHelper.h"
#include "../common/DTOShipStatus.h"
#include "../SDLtoolkit/widgets/Ship.h"

Application::Application() {
	Logger::log("Application", "Application", "Iniciando Aplicación");
	this->initializeSDL();
	game = NULL;
	this->conectado = false;
	this->server = NULL;
	this->cliente = NULL;
}

Application::~Application() {
	if (this->activeWindow == NULL)
		delete activeWindow;

	delete this->game;

	delete this->engine;

	Logger::log("Application", "Application", "Finalizando Aplicación");
}

//TODO mover a factory
void Application::switchActiveWindow(WindowID id, std::string param) {
	Window* aux = NULL;

	switch (id) {
	case play:
		aux = this->activeWindow;
		this->activeWindow = WindowHelper::playSceneWindow(game);
		delete aux;
		break;
	case EXIT:
		aux = this->activeWindow;
		this->activeWindow = WindowHelper::exitWindow();
		delete aux;

		break;

	default:
		Logger::log("Application", "switchActiveWindow",
				"Se desecho un evento.");
		break;
	}
}

void Application::initializeSDL() {
	int width = 0, height = 0;
	Persistor::getDimensions(width,height);
	this->engine = new SDLWrapper("Inicio", width, height, BITS_PER_PIXEL);
	this->engine->start();
}

void* Application::run() {
	Timer fps;
	bool quit = false;

	//Create the play scene
	SpaceBattleInterface* game = new SpaceBattleInterface(MAIN_SCENARIO,true);
	this->activeWindow = WindowHelper::gameWindow();

	Event* event = NULL;
	while (quit == false) {

		fps.start();
		while (this->engine->pullEvent(&event)) {

			/*
			if(this->cliente != NULL){
				Logger::log("Application","run","Soy cliente : " + this->cliente->getClienteName() + " . Evento Recibido : " + UtilString::intToString(event.getType()));
			}
			*/

			switch (event->getType()) {

			//Logger::log("Application", "run","Evento del tipo: " + UtilString::intToString(event->getType()));


			case UserEvent:

				switch (event->getEvent()->user.code) {
					case Ship_Status:
						if(this->activeWindow->getWindowType() == play){
							PlayScenarioPanel* play = (PlayScenarioPanel*)(this->activeWindow->getScenarioContainer());

							//Actualizo el estado del ship en el escenario panel
							DTOShipStatus* dtoStatusShip = (DTOShipStatus*)Translator::translate(*event);
							ShipStatus* shipStatus = new ShipStatus(dtoStatusShip);

							play->addPlayer(shipStatus);

						}

						break;
					case NewUser:{
						std::string newPlayer;
						newPlayer = *((std::string*)event->getEvent()->user.data1);

						//PARCHE
						event->setSender(newPlayer);

						std::stringstream ss;
						ss << "Sender: " << event->getSender();

						Logger::log("application", "run", &ss);

						//Agrego el nuevo cliente conectado
						//TODO fijarse el tema de las coordenadas, que el server de una coordenadas random
						//para darle mas onda al tema

						if(this->activeWindow->getWindowType() == play){
							PlayScenarioPanel* play = (PlayScenarioPanel*)(this->activeWindow->getScenarioContainer());

							//TODO ver como se va a implementar esto en el futuro!!
							//play->addPlayer(newPlayer,30,30);

							//Envio mi estado
							this->cliente->setMensaje((DTO*)play->getShipStatus().transformInDTO());
						}
					}
						break;

				}
				handleUserEvent(*event);

				break;
			case Resize:
				this->engine->updateVideoMode(event->getW(), event->getH());
				break;
			case Quit:
				this->conectado = false;
				quit = true;
				break;
			case KeyPressed:
					//Solo me interezan los eventos del play windows, el resto no.
					if (game != NULL && activeWindow->getWindowType() == play) {

						//Propaga los eventos a los widgets de la ventana
						//TODO only for TP2. No remote players yet
						//this->cliente->setMensaje(Translator::translate(event));

					}
					this->activeWindow->handleEvent(*event);
					break;
			case KeyReleased:
				//Solo me interezan los eventos del play windows, el resto no.
				if (game != NULL && activeWindow->getWindowType() == play) {

					//Propaga los eventos a los widgets de la ventana
					//TODO only for TP2. No remote players yet
					//this->cliente->setMensaje(Translator::translate(event));
				}
				this->activeWindow->handleEvent(*event);
				break;

			default:
				this->activeWindow->handleEvent(*event);

				break;
			}
		}
		this->conectado = false;

		this->engine->clearScreen();

		this->activeWindow->resolve();

		this->activeWindow->showOn(this->engine->getMainSurface()->getSurface());

		this->engine->refreshScreen();

        if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
        {
            SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
        }

        //Free memory
        if(event != NULL){
        	delete event;
        	//Avoid double free
        	event = NULL;
        }

	}

	//Borro el juego
	delete game;
	terminar();

	return NULL;

}

//TODO a una factory afuera
void Application::handleUserEvent(Event& event) {
	std::string screenTitle;
	std::string host;
	int separador;
	std::string port;
	SocketProtocoloPlano* socket;
	std::string user;

	//TODO esto es feo, ver como mejorar
	switch (event.getCode()) {
	case CREATE:
		//Puede definir de donde vienen las conexiones, ip y puerto
		//Formato esperado= IP:PUERTO
		separador = event.getData().find_first_of(':');
		host = event.getData().substr(0, separador);
		port = event.getData().substr(separador + 1);
		user = event.getData2();
		engine->setPlayerName(user);

		break;
	case CONNECT: {
		//Puede definir de donde vienen las conexiones, ip y puerto
		//Formato esperado= IP:PUERTO
		separador = event.getData().find_first_of(':');
		host = event.getData().substr(0, separador);
		port = event.getData().substr(separador + 1);
		user = event.getData2();
		engine->setPlayerName(user);

		//Hago un push del evento conexion.
		SDL_Event event;
		event.type = SDL_USEREVENT;
		event.user.code = NewUser;
		event.user.data1 = new std::string(user);

		SDL_PushEvent(&event);

		break;
	}

	default:
		//No hacer naranja
		break;
	}

	switch (event.getCode()) {
	case edit:
		this->switchActiveWindow(event.getCode(), event.getData());

		screenTitle = "SpaceBattle3000: Editar escenario [";
		screenTitle += event.getData();
		screenTitle += "]";
		event.eraseData();
		this->engine->changeScreenTitle(screenTitle);
		break;
	case load:
		this->switchActiveWindow(event.getCode(), STRING_EMPTY);
		this->engine->changeScreenTitle("Inicio");
		break;
	case PRINCIPAL:
		this->switchActiveWindow(event.getCode(), STRING_EMPTY);
		this->engine->changeScreenTitle("Start Game");
		break;
	case connection:
		this->switchActiveWindow(event.getCode(), STRING_EMPTY);
		this->engine->changeScreenTitle("Connection Game");
		break;
	case CREATE:

		//Avoid to have multiple instance of server running in the same machine
		if (this->server != NULL && this->server->isRunning()) {
			Logger::log("Application", "handleUserEvent",
					"Otra instancia de server esta corriendo.");
			break;
		}

		game = new SpaceBattleInterface(event.getData2(), true);
		this->server = new Servidor(game);

		//Inicio el listener
		server->iniciar(UtilString::stringToInt(port), SERVER_MAX_QUEUE);

		//Inicio el server que replica los mensajes
		server->start();

		Logger::log("Application", "handleUserEvent", "Server iniciado");
		break;
	case CONNECT:
		socket = new SocketProtocoloPlano();
		if (socket->connect(host, UtilString::stringToInt(port)) == 0) {
			conectado = true;
			Logger::log("Application", "handleUserEvent", "Conexion iniciada");
		} else {
			Logger::log("Application", "handleUserEvent", "Fallo la Conexion");
			conectado = false;
			break;
		}

		//En la misma instancia puede tener un servidor y un cliente
		if (game == NULL) {
			game = new SpaceBattleInterface(user);
		}

		cliente = new Client(user, host, game, socket, &conectado);

		this->switchActiveWindow(play, STRING_EMPTY);
		this->engine->changeScreenTitle("Jugando");

		cliente->start();

		break;

	case UserKeyPressed:
		//Logger::log("Application", "run",
			//	"Evento del tipo :" + UtilString::intToString(event.getType()));
		//Solo me interezan los eventos del play windows, el resto no.
		if (game != NULL && activeWindow->getWindowType() == play) {

			//Propaga los eventos a los widgets de la ventana
			this->cliente->setMensaje(Translator::translate(event));
			activeWindow->handleEvent(event);
		}
		break;
	case UserKeyReleased:
		//Solo me interezan los eventos del play windows, el resto no.
		if (game != NULL && activeWindow->getWindowType() == play) {

			//Propaga los eventos a los widgets de la ventana
			this->cliente->setMensaje(Translator::translate(event));
			activeWindow->handleEvent(event);
		}
		break;
	default:
		break;
	}
}

void Application::terminar() {

	//Pongo la pantalla de cerrando.
	delete this->activeWindow;
	this->activeWindow = WindowHelper::exitWindow();
	this->engine->refreshScreen();

	if (this->server != NULL) {
		this->server->terminar();
		this->server->join();
		this->server = NULL;
	}

	if (this->cliente != NULL) {
		this->cliente->join();
		this->cliente = NULL;
	}

	delete this->activeWindow;

	this->engine->stop();
}

